<template>
  <div :id="demo.demoId" class="ti-b-a ti-br-sm ti-wp100" :class="currDemoId === demo.demoId ? 'b-a-success' : ''">
    <div class="ti-px24 ti-py20">
      <!-- DEMO 的标题 + 说明desc+  示例wcTag -->
      <div class="ti-f-r ti-f-pos-between ti-f-box-end ti-pb20">
        <div class="ti-f18 ti-cur-hand">{{ demo.name[langKey] }}</div>
        <div>
          <tiny-tooltip placement="top" :append-to-body="false" :content="copyTip">
            <i
              :class="copyIcon"
              class="h:c-success ti-w16 ti-h16 ti-cur-hand"
              @click="copyCode(demo)"
              @mouseout="resetTip()"
            />
          </tiny-tooltip>
          <tiny-tooltip
            placement="top"
            :append-to-body="false"
            :content="demo.isOpen ? i18nByKey('hideCode') : i18nByKey('showCode')"
          >
            <i
              :class="!!demo.isOpen ? 'i-ti-codeslash' : 'i-ti-code'"
              class="ti-ml8 h:c-success ti-w16 ti-h16 ti-cur-hand"
              @click="toggleDemoCode(demo)"
            />
          </tiny-tooltip>
          <tiny-tooltip placement="top" :append-to-body="false" :content="i18nByKey('playground')">
            <i class="i-ti-playground ml8 h:c-success ti-w16 ti-h16 ti-cur-hand" @click="openPlayground(demo)" />
          </tiny-tooltip>
        </div>
      </div>
      <component :is="getDescMd(demo)" class="ti-mb16 ti-f14" />

      <div v-if="isMobileFirst" class="pc-demo-container">
        <tiny-button @click="openPlayground(demo, false)">多端预览</tiny-button>
      </div>
      <div v-else-if="demoConfig.isMobile" class="phone-container">
        <div class="mobile-view-container">
          <component :is="cmp" />
        </div>
      </div>
      <div v-else class="pc-demo-container">
        <div class="pc-demo">
          <component :is="cmp" />
        </div>
      </div>
    </div>
    <!-- demo 打开后的示例代码  细滚动时，width:fit-content; -->
    <div v-if="demo.isOpen" class="ti-px24 ti-py20 ti-b-t-lightless">
      <div>
        <tiny-tabs v-model="tabValue" class="code-tabs">
          <tiny-tab-item v-for="(file, idx) in files" :key="file.fileName" :name="'tab' + idx" :title="file.fileName">
            <async-highlight :code="file.code"></async-highlight>
          </tiny-tab-item>
        </tiny-tabs>
      </div>
    </div>
  </div>
</template>

<script lang="jsx">
import { defineComponent, reactive, computed, toRefs, shallowRef, onMounted, watch, nextTick, inject, ref } from 'vue'
import { i18nByKey, getWord } from '@/i18n'
import { $split, appData, fetchDemosFile } from '@/tools'
import { Tooltip as TinyTooltip, Tabs as TinyTabs, TabItem as TinyTabItem, Button as TinyButton } from '@opentiny/vue'
import { languageMap, vueComponents, getWebdocPath, staticDemoPath } from './cmpConfig'
import { router } from '@/router.js'
import demoConfig from '@demos/config.js'
import { useApiMode, useTemplateMode } from '@/tools'
import useTheme from '@/tools/useTheme'
import AsyncHighlight from './async-highlight.vue'

const { apiModeState, apiModeFn } = useApiMode()

export default defineComponent({
  name: 'Demo',
  props: ['demo'],
  components: {
    TinyTooltip,
    TinyTabs,
    TinyTabItem,
    TinyButton,
    AsyncHighlight
  },
  setup(props) {
    const { templateModeState } = useTemplateMode()
    const { currentThemeKey } = useTheme()
    const isMobileFirst = computed(() => {
      return templateModeState.mode === 'mobile-first'
    })

    const getDemoCodeFn = async (demo, forceUpdate) => {
      // 获取code代码文本
      if (!demo.files || forceUpdate) {
        const cmpId = router.currentRoute.value.params.cmpId
        const promises = demo.codeFiles.map(async (fileName) => {
          // 切换option-api和composition-api
          const demoName = apiModeFn.getDemoName(`${getWebdocPath(cmpId)}/${fileName}`)
          let code = ''

          const path = isMobileFirst.value ? `@demos/mobile-first/app/${demoName}` : `${staticDemoPath}/${demoName}`
          code = await fetchDemosFile(path)
            .then((code) => {
              return code
            })
            .catch(() => {
              return `${demoName}示例资源不存在，请检查文件名是否正确？`
            })
          const ext = $split(fileName, '.', -1)
          const language = languageMap[ext] || ''
          return { code, fileName, language }
        })
        demo.files = await Promise.all(promises)
        return demo.files
      }
      return demo.files
    }
    const state = reactive({
      tabValue: 'tab0',
      cmpId: router.currentRoute.value.params.cmpId,
      langKey: getWord('zh-CN', 'en-US'),
      currDemoId: computed(() => {
        let hash = router.currentRoute.value.hash?.slice(1)

        // 兼容/#hashName这种模式
        if (hash.includes('/')) {
          hash = hash.slice(1)
        }
        return hash
      }),
      copyTip: i18nByKey('copyCode'),
      copyIcon: 'i-ti-copy'
    })

    const cmp = shallowRef(null)

    const showPreview = inject('showPreview')

    const fn = {
      getDescMd(demo) {
        // desc字段是一段html
        const desc = demo.desc[state.langKey].trim()
        return <div class="demo-desc" v-html={desc}></div>
      },
      async toggleDemoCode(demo) {
        // 第一次打开时，要请求一下相应的codeFiles .存储到files属性下
        if (!demo.files) {
          await this.getDemoCode(demo)
          demo.isOpen = true
        } else {
          demo.isOpen = !demo.isOpen
        }

        await nextTick()
        window.dispatchEvent(new Event('resize'))
      },
      async copyCode(demo) {
        if (demo.isOpen) {
          const idx = parseInt(state.tabValue.slice(3))

          navigator.clipboard.writeText(demo.files[idx].code)
        } else {
          await this.getDemoCode(demo)

          navigator.clipboard.writeText(demo.files[0].code)
        }
        state.copyTip = i18nByKey('copyCodeOk')
        state.copyIcon = 'i-ti-check'
      },
      resetTip() {
        setTimeout(() => {
          state.copyTip = i18nByKey('copyCode')
          state.copyIcon = 'i-ti-copy'
        }, 300)
      },
      getDemoCode(demo) {
        // 获取code代码文本
        return getDemoCodeFn(demo)
      },
      openPlayground(demo, open = true) {
        const cmpId = router.currentRoute.value.params.cmpId
        const tinyTheme = templateModeState.isSaas ? 'saas' : currentThemeKey.value.split('-')[1]
        const openModeQuery = open ? '' : '&openMode=preview'
        // TODO: 目前mf只有Options写法，后续再放开compositon
        const url = `${import.meta.env.VITE_PLAYGROUND_URL}?cmpId=${cmpId}&fileName=${
          demo.codeFiles[0]
        }&apiMode=Options&mode=${templateModeState.mode}&theme=${tinyTheme}${openModeQuery}`

        if (open) {
          window.open(url)
        } else {
          showPreview(url)
        }
      }
    }

    onMounted(async () => {
      const demoName = apiModeFn.getDemoName(`${getWebdocPath(state.cmpId)}/${props.demo.codeFiles[0]}`)

      if (vueComponents[demoName]) {
        cmp.value = (await vueComponents[demoName]()).default
      } else {
        const log = `${demoName}示例资源不存在，请检查文件名是否正确？`
        cmp.value = <div>{log}</div>
      }
    })

    const demos = ref(props.demo)
    const files = ref([])

    watch(
      () => props.demo,
      () => {
        demos.value = props.demo
        if (props.demo.files) {
          files.value = props.demo.files
        }
      },
      { deep: true }
    )

    watch(
      () => apiModeState.apiMode,
      () => {
        if (props.demo.files?.length > 0) {
          getDemoCodeFn(props.demo, true).then((demoFiles) => {
            files.value = demoFiles
          })
        }
      }
    )

    return { ...toRefs(state), ...fn, appData, vueComponents, demoConfig, cmp, isMobileFirst, i18nByKey, files }
  }
})
</script>

<style lang="less" scoped>
.demo-desc {
  font-size: 16px;
  line-height: 1.7em;
  margin: 12px 0;

  :deep(code) {
    color: #476582;
    padding: 4px 8px;
    margin: 0 4px;
    font-size: 0.85em;
    background-color: rgba(27, 31, 35, 0.05);
    border-radius: 3px;
  }

  a {
    text-decoration: none;
    color: #5e7ce0;
    cursor: pointer;
  }

  ul,
  ol {
    list-style: disc;
    list-style-position: inside;
  }
}

.theme-dark {
  .demo-desc {
    code {
      background: #333333;
    }
  }
}

.b-a-success {
  animation: border-trans 3s;
}

@keyframes border-trans {
  0% {
    border: 1px solid #5073e5;
    background: none;
  }

  50% {
    background: rgba(255, 95, 85, 0.1);
    border: 1px solid rgba(255, 95, 88, 0.6);
  }

  100% {
    border: 1px solid #5073e5;
    background: none;
  }
}

.pc-demo-container {
  display: flex;
  flex-direction: column;
  .pc-demo {
    flex: 1;
  }
}
.phone-container {
  margin: auto;
  width: 395px;
  height: 773px;
  padding: 20px;
  border: 0;
  position: relative;
  background: url()
    no-repeat center top;
  background-size: contain;

  .mobile-view-container {
    width: 360px;
    height: 613px;
    position: absolute;
    left: 11px;
    top: 79px;
    transform: i18nByKeyX(0);
    overflow: hidden;
  }
}

.code-tabs {
  .tiny-tabs__content {
    padding: 0;
  }
}

.code-preview-box {
  overflow: auto;
  padding: 20px 5px;
  background-color: #f5f6f8;
}
</style>
